﻿using Plugin.BLE;
using Plugin.BLE.Abstractions;
using Plugin.BLE.Abstractions.Contracts;
using Plugin.BLE.Abstractions.EventArgs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace RaceHFTools.BluetoothLE
{
    public class Controller
    {
        public EventHandler<IDevice> OnDiscover, OnConnect, OnDisconnect;
        private readonly IAdapter adapter = CrossBluetoothLE.Current.Adapter;

        public Controller()
        {
            adapter.DeviceDiscovered += OnDisconverHandler;
            adapter.DeviceConnected += OnConnectedHandler;
            adapter.DeviceDisconnected += OnDisconnectHandler;
            adapter.DeviceConnectionLost += OnDisconnectHandler;
        }

        ~Controller()
        {
            adapter.DeviceDiscovered -= OnDisconverHandler;
            adapter.DeviceConnected -= OnConnectedHandler;
            adapter.DeviceDisconnected -= OnDisconnectHandler;
            adapter.DeviceConnectionLost -= OnDisconnectHandler;
        }

        void OnDisconverHandler(object sender, DeviceEventArgs args)
        {
            OnDiscover?.Invoke(sender, args.Device);
        }

        void OnConnectedHandler(object sender, DeviceEventArgs args)
        {
            OnConnect?.Invoke(sender, args.Device);
        }

        void OnDisconnectHandler(object sender, DeviceEventArgs args)
        {
            OnDisconnect?.Invoke(sender, args.Device);
        }

        static public IDevice FindBestRssiDevice(IDevice[] targets)
        {
            IDevice best_target = null;
            int rssi = -127;

            foreach (IDevice target in targets)
            {
                if (target.Rssi > rssi)
                {
                    best_target = target;
                    rssi = best_target.Rssi;
                }
            }

            return best_target;
        }

        public async Task Scan(int duration = 5000, Func<IDevice, bool> filter = null, CancellationToken ct = default)
        {
            adapter.ScanTimeout = duration;
            adapter.ScanMode = ScanMode.LowLatency;
            await adapter.StopScanningForDevicesAsync();
            await adapter.StartScanningForDevicesAsync(deviceFilter: filter, cancellationToken: ct);
        }

        public bool IsConnect(IDevice target)
        {
            foreach (IDevice x in adapter.ConnectedDevices)
            {
                if (x == target)
                    return true;
            }

            return false;
        }

        public bool IsConnect(string name_filter)
        {
            foreach (IDevice x in adapter.ConnectedDevices)
            {
                if (!string.IsNullOrEmpty(x.Name) && x.Name.Contains(name_filter))
                    return true;
            }

            return false;
        }

        public async Task<bool> Connect(IDevice target, CancellationToken ct = default)
        {
            if (target == null)
                return false;

            await adapter.ConnectToDeviceAsync(target, new ConnectParameters(false, true), ct);

            return IsConnect(target);
        }

        public async Task<bool> Disconnect(IDevice target)
        {
            if (target == null)
                return false;

            if (!adapter.ConnectedDevices.Contains(target))
                return false;

            await adapter.DisconnectDeviceAsync(target);

            return IsConnect(target);
        }

        public async Task<bool> Disconnect(string name_filter)
        {
            if (string.IsNullOrEmpty(name_filter))
                return false;

            foreach (IDevice x in adapter.ConnectedDevices)
            {
                if (!string.IsNullOrEmpty(x.Name) && x.Name.Contains(name_filter))
                {
                    await adapter.DisconnectDeviceAsync(x);
                }
            }

            return IsConnect(name_filter);
        }
    }
}
